package benchmarks;

import java.io.IOException;
import java.lang.reflect.Type;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import edu.vub.at.objects.ATObject;
import edu.vub.at.objects.ATTable;
import edu.vub.at.objects.ATTypeTag;
import edu.vub.at.objects.grammar.ATBegin;
import edu.vub.at.objects.grammar.ATExpression;
import edu.vub.at.objects.grammar.ATSymbol;
import edu.vub.at.objects.natives.NATTable;
import edu.vub.at.objects.natives.OBJLexicalRoot;
import edu.vub.at.parser.ParserFactory;

public class GSonLib implements TestMethods {
	public static Gson gson;

	public GSonLib() {
		// gson = new Gson();

		// http://chaos.unknown-entity.com/?p=124
		gson = new GsonBuilder()
				.registerTypeAdapter(ATExpression.class,
						new InterfaceAdapter<ATExpression>())
				.registerTypeAdapter(ATSymbol.class,
						new InterfaceAdapter<ATSymbol>())
				.registerTypeAdapter(ATExpression.class,
						new InterfaceAdapter<ATExpression>())
				.registerTypeAdapter(ParserFactory.class,
						new InterfaceAdapter<ParserFactory>())
				.registerTypeAdapter(ATTable.class,
						new InterfaceAdapter<ATTable>())
				.registerTypeAdapter(ATObject.class,
						new InterfaceAdapter<ATObject>())
				.registerTypeAdapter(ATBegin.class,
						new InterfaceAdapter<ATBegin>())
				.registerTypeAdapter(ATTypeTag.class,
						new InterfaceAdapter<ATTypeTag>())
				.registerTypeAdapter(OBJLexicalRoot.class,
						new InterfaceAdapter<OBJLexicalRoot>())
				// .setPrettyPrinting()
				.create();

	}

	@Override
	public byte[] serialize(Object o) throws IOException {
		return gson.toJson(o).getBytes("UTF-8");
	}

	@Override
	public Object deserialize(byte[] b, Class c) throws IOException,
			ClassNotFoundException {
		return gson.fromJson(new String(b, "UTF-8"), c);
	}

	@Override
	public Object copy(Object o) throws IOException, ClassNotFoundException {
		return deserialize(serialize(o), o.getClass());
	}

	public class InterfaceAdapter<T> implements JsonSerializer<T>,
			JsonDeserializer<T> {

		@Override
		public final JsonElement serialize(final T object,
				final Type interfaceType, final JsonSerializationContext context) {
			final JsonObject member = new JsonObject();
			member.addProperty("type", object.getClass().getName());
			member.add("data", context.serialize(object));
			return member;
		}

		@Override
		public final T deserialize(final JsonElement elem,
				final Type interfaceType,
				final JsonDeserializationContext context)
				throws JsonParseException {
			final JsonObject member = (JsonObject) elem;
			final JsonElement typeString = get(member, "type");
			final JsonElement data = get(member, "data");
			final Type actualType = typeForName(typeString);
			
			System.out.println(typeString.getAsString());
			//TODO IF TEST VOOR OBJLexical root INSTANCE en NATTable.EMPTY
//			if(typeString.getAsString().equals("edu.vub.at.objects.natives.NATTable")){ 
//				return (T) NATTable.EMPTY;
//			}
			if (typeString.getAsString().equals(
					"edu.vub.at.objects.natives.OBJLexicalRoot")) {
				return (T) OBJLexicalRoot._INSTANCE_;
			}
			return context.deserialize(data, actualType);
		}

		private Type typeForName(final JsonElement typeElem) {
			try {
				return Class.forName(typeElem.getAsString());
			} catch (ClassNotFoundException e) {
				throw new JsonParseException(e);
			}
		}

		private JsonElement get(final JsonObject wrapper,final String memberName) {
			final JsonElement elem = wrapper.get(memberName);
			if (elem == null) {
				throw new JsonParseException("no '" + memberName
						+ "' member found in json file.");
			}
			return elem;
		}
	}

}
